#include "pj_parent.hpp"

/**************************************   CONSTANTS  **************************************/
static const int DEBUG_PARENT_PROCESS_EXIT_US = 500000000; 			//for debug: the time of the whole program runs
static const int MANAGER_INTERVAL = 15000000; 						//the interval time that manager try to manage processes

static const int PARENT_WAIT_MANAGER_EXIT_INTERVALTIME = 1000000;   //parent wait for manager process exit interval time

/*
 * process of parent, head function, with exit() inside
 * para: sem_t* sem_par_man, struct_val_parent_manager* svpm, int mid(manager id)
 * return: none
 */
void parentProcess(sem_t* sem_par_man, struct_val_parent_manager* svpm, int mid){
	cout << "parent: start, waiting for all child process died after usleep" << DEBUG_PARENT_PROCESS_EXIT_US << endl;
	cout << "parent: find that the manager process id is: " << mid << endl;
	
	while(true){
	//for (int i = 0; i < DEBUG_PARENT_PROCESS_EXIT_US/MANAGER_INTERVAL; ++i){
		usleep(MANAGER_INTERVAL);
		if (kill(mid, SIGUSR1) == -1){
			cerr << "parent: try to kill SIGUSR1 manager process, failed" << endl;
			continue;
		}else cout << "parent: kill SIGUSR1 manager process, try ok" << endl;
	}
	
	sem_wait(sem_par_man);
	svpm->isShutdown = true;
	cout << "parent: think that the processPool should have a rest" << endl;
	sem_post(sem_par_man);
	
	//note: no timeout setting!, should consider in later version
	while(true)
	{
		int status;
		int ret = waitpid(mid, &status, WNOHANG);
		if (ret == -1){
			cerr << "waitpid" << endl;
			cout << "parent: find that manager process: " << ret << " has exited" << endl;
			break;
		}
		else if (ret == 0){
			cout << "parent: find that manager process didn't exit yet" << endl;
			usleep(PARENT_WAIT_MANAGER_EXIT_INTERVALTIME);
			continue;
		}
		else{
			if (WIFEXITED(status)) cout << "parent: find that manager exit with "<< WEXITSTATUS(status) << endl;
			else if (WIFSIGNALED(status)) cout << "parent: find that manager killed by "<< WTERMSIG(status) << endl;
			break;
		}
	}
}

/*
 * entry function
 * note: if run into some unexpected return -1, the program will automatically exit some temp,
 *      e.g. fileFDs, ..., so I didn't write them explicitly
 * para: none
 * return (exit code), 0: ok; -1: error
 */
int main(){
	//change workdirectory
	if (chdir(WORKDIR.c_str())) exit(2);
	
	//clear sems
	if (sem_unlink("/bla_psem_managerShutdown") == -1) cout << "parent: /bla_psem_managerShutdown not occur" << endl;
	else cout << "parent: /bla_psem_managerShutdown has been deleted:)" << endl;
	
	//sems, struct_sems init
	sem_t* sem_par_man = sem_open("/bla_psem_managerShutdown", O_CREAT, 0644, 1);  //exclusive signal
	if (sem_par_man == SEM_FAILED){
		cerr << "sem_open" << endl;
		return -1;
	}
	
	//mmaps, struct_workers init
	int fd_vpm = open("temp_struct_val_parent_manager.txt", O_CREAT|O_RDWR|O_TRUNC, 0644);
	if (fd_vpm < 0) {
		cerr << "open" << endl;
		return false;
	}
	unlink("temp_struct_val_parent_manager.txt");
	if (ftruncate(fd_vpm, sizeof(struct_val_parent_manager)) == -1){
		cerr << "ftruncate" << endl;
		return false;
	}
	struct_val_parent_manager* svpm = (struct_val_parent_manager* )mmap(NULL, sizeof(struct_val_parent_manager), PROT_READ|PROT_WRITE, MAP_SHARED, fd_vpm, 0);
	if (svpm == MAP_FAILED)
	{
		cerr << "mmap" << endl;
		return false;
	}
	close(fd_vpm);

	//fork
	int managerid = fork();
	if (managerid == -1){
		cerr << "fork" << endl;
		return -1;
	}
	else if(managerid == 0){  //manager
		ManagerProcess(sem_par_man, svpm); 				//with exit inside
		exit(-3);									//dump exit
	}else{  //parent
		//run into parent process
		parentProcess(sem_par_man, svpm, managerid);           	//without exit inside
		
		//exit
		if (sem_unlink("/bla_psem_managerShutdown") == -1){
			cerr << "sem_unlink" << endl;
			return -1;
		}
		munmap(svpm, sizeof(struct_val_parent_manager));
		return 0;
	}
}

